/*
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
Michaël Peeters and Gilles Van Assche. For more information, feedback or
questions, please refer to our website: http://keccak.noekeon.org/

Implementation by Ronny Van Keer,
hereby denoted as "the implementer".

To the extent possible under law, the implementer has waived all copyright
and related or neighboring rights to the source code in this file.
http://creativecommons.org/publicdomain/zero/1.0/
*/

// WARNING: This implementation assumes a little-endian platform. Support for
// big-endinanness is not yet implemented.

#include <string.h>

#include "Keccak-simple-settings.h"
#define cKeccakR_SizeInBytes (cKeccakR / 8)
#include "crypto_hash.h"
#ifndef crypto_hash_BYTES
#ifdef cKeccakFixedOutputLengthInBytes
#define crypto_hash_BYTES cKeccakFixedOutputLengthInBytes
#else
#define crypto_hash_BYTES cKeccakR_SizeInBytes
#endif
#endif
#if (crypto_hash_BYTES > cKeccakR_SizeInBytes)
#error "Full squeezing not yet implemented"
#endif

#if (cKeccakB == 1600)
typedef unsigned long long UINT64;
typedef UINT64 tKeccakLane;
#define cKeccakNumberOfRounds 24
#elif (cKeccakB == 800)
typedef unsigned int UINT32;
// WARNING: on 8-bit and 16-bit platforms, this should be replaced by:
// typedef unsigned long       UINT32;
typedef UINT32 tKeccakLane;
#define cKeccakNumberOfRounds 22
#elif (cKeccakB == 400)
typedef unsigned short UINT16;
typedef UINT16 tKeccakLane;
#define cKeccakNumberOfRounds 20
#elif (cKeccakB == 200)
typedef unsigned char UINT8;
typedef UINT8 tKeccakLane;
#define cKeccakNumberOfRounds 18
#else
#error "Unsupported Keccak-f width"
#endif

#define cKeccakLaneSizeInBits (sizeof(tKeccakLane) * 8)

#define ROL(a, offset)                                        \
  ((((tKeccakLane)a) << ((offset) % cKeccakLaneSizeInBits)) ^ \
   (((tKeccakLane)a) >>                                       \
    (cKeccakLaneSizeInBits - ((offset) % cKeccakLaneSizeInBits))))
#if ((cKeccakB / 25) == 8)
#define ROL_mult8(a, offset) ((tKeccakLane)a)
#else
#define ROL_mult8(a, offset) ROL(a, offset)
#endif
void KeccakF(tKeccakLane *state, const tKeccakLane *in, int laneCount);

int simplest_ot_crypto_hash(unsigned char *out, const unsigned char *in,
                            unsigned long long inlen) {
  tKeccakLane state[5 * 5];
#if (crypto_hash_BYTES >= cKeccakR_SizeInBytes)
#define temp out
#else
  unsigned char temp[cKeccakR_SizeInBytes];
#endif

  memset(state, 0, sizeof(state));

  for (/* empty */; inlen >= cKeccakR_SizeInBytes;
       inlen -= cKeccakR_SizeInBytes, in += cKeccakR_SizeInBytes) {
    KeccakF(state, (const tKeccakLane *)in,
            cKeccakR_SizeInBytes / sizeof(tKeccakLane));
  }

  //    padding
  memcpy(temp, in, (size_t)inlen);
  temp[inlen++] = 1;
  memset(temp + inlen, 0, cKeccakR_SizeInBytes - (size_t)inlen);
  temp[cKeccakR_SizeInBytes - 1] |= 0x80;
  KeccakF(state, (const tKeccakLane *)temp,
          cKeccakR_SizeInBytes / sizeof(tKeccakLane));
  memcpy(out, state, crypto_hash_BYTES);
#if (crypto_hash_BYTES >= cKeccakR_SizeInBytes)
#undef temp
#endif

  return (0);
}

const tKeccakLane KeccakF_RoundConstants[cKeccakNumberOfRounds] = {
    (tKeccakLane)0x0000000000000001ULL,
    (tKeccakLane)0x0000000000008082ULL,
    (tKeccakLane)0x800000000000808aULL,
    (tKeccakLane)0x8000000080008000ULL,
    (tKeccakLane)0x000000000000808bULL,
    (tKeccakLane)0x0000000080000001ULL,
    (tKeccakLane)0x8000000080008081ULL,
    (tKeccakLane)0x8000000000008009ULL,
    (tKeccakLane)0x000000000000008aULL,
    (tKeccakLane)0x0000000000000088ULL,
    (tKeccakLane)0x0000000080008009ULL,
    (tKeccakLane)0x000000008000000aULL,
    (tKeccakLane)0x000000008000808bULL,
    (tKeccakLane)0x800000000000008bULL,
    (tKeccakLane)0x8000000000008089ULL,
    (tKeccakLane)0x8000000000008003ULL,
    (tKeccakLane)0x8000000000008002ULL,
    (tKeccakLane)0x8000000000000080ULL
#if (cKeccakB >= 400)
    ,
    (tKeccakLane)0x000000000000800aULL,
    (tKeccakLane)0x800000008000000aULL
#if (cKeccakB >= 800)
    ,
    (tKeccakLane)0x8000000080008081ULL,
    (tKeccakLane)0x8000000000008080ULL
#if (cKeccakB == 1600)
    ,
    (tKeccakLane)0x0000000080000001ULL,
    (tKeccakLane)0x8000000080008008ULL
#endif
#endif
#endif
};

void KeccakF(tKeccakLane *state, const tKeccakLane *in, int laneCount) {
  {
    while (--laneCount >= 0) {
      state[laneCount] ^= in[laneCount];
    }
  }

  {
    tKeccakLane Aba, Abe, Abi, Abo, Abu;
    tKeccakLane Aga, Age, Agi, Ago, Agu;
    tKeccakLane Aka, Ake, Aki, Ako, Aku;
    tKeccakLane Ama, Ame, Ami, Amo, Amu;
    tKeccakLane Asa, Ase, Asi, Aso, Asu;
    tKeccakLane BCa, BCe, BCi, BCo, BCu;
    tKeccakLane Da, De, Di, Do, Du;
    tKeccakLane Eba, Ebe, Ebi, Ebo, Ebu;
    tKeccakLane Ega, Ege, Egi, Ego, Egu;
    tKeccakLane Eka, Eke, Eki, Eko, Eku;
    tKeccakLane Ema, Eme, Emi, Emo, Emu;
    tKeccakLane Esa, Ese, Esi, Eso, Esu;
#define round laneCount

    // copyFromState(A, state)
    Aba = state[0];
    Abe = state[1];
    Abi = state[2];
    Abo = state[3];
    Abu = state[4];
    Aga = state[5];
    Age = state[6];
    Agi = state[7];
    Ago = state[8];
    Agu = state[9];
    Aka = state[10];
    Ake = state[11];
    Aki = state[12];
    Ako = state[13];
    Aku = state[14];
    Ama = state[15];
    Ame = state[16];
    Ami = state[17];
    Amo = state[18];
    Amu = state[19];
    Asa = state[20];
    Ase = state[21];
    Asi = state[22];
    Aso = state[23];
    Asu = state[24];

    for (round = 0; round < cKeccakNumberOfRounds; round += 2) {
      //    prepareTheta
      BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa;
      BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase;
      BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi;
      BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso;
      BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu;

      // thetaRhoPiChiIotaPrepareTheta(round  , A, E)
      Da = BCu ^ ROL(BCe, 1);
      De = BCa ^ ROL(BCi, 1);
      Di = BCe ^ ROL(BCo, 1);
      Do = BCi ^ ROL(BCu, 1);
      Du = BCo ^ ROL(BCa, 1);

      Aba ^= Da;
      BCa = Aba;
      Age ^= De;
      BCe = ROL(Age, 44);
      Aki ^= Di;
      BCi = ROL(Aki, 43);
      Amo ^= Do;
      BCo = ROL(Amo, 21);
      Asu ^= Du;
      BCu = ROL(Asu, 14);
      Eba = BCa ^ ((~BCe) & BCi);
      Eba ^= (tKeccakLane)KeccakF_RoundConstants[round];
      Ebe = BCe ^ ((~BCi) & BCo);
      Ebi = BCi ^ ((~BCo) & BCu);
      Ebo = BCo ^ ((~BCu) & BCa);
      Ebu = BCu ^ ((~BCa) & BCe);

      Abo ^= Do;
      BCa = ROL(Abo, 28);
      Agu ^= Du;
      BCe = ROL(Agu, 20);
      Aka ^= Da;
      BCi = ROL(Aka, 3);
      Ame ^= De;
      BCo = ROL(Ame, 45);
      Asi ^= Di;
      BCu = ROL(Asi, 61);
      Ega = BCa ^ ((~BCe) & BCi);
      Ege = BCe ^ ((~BCi) & BCo);
      Egi = BCi ^ ((~BCo) & BCu);
      Ego = BCo ^ ((~BCu) & BCa);
      Egu = BCu ^ ((~BCa) & BCe);

      Abe ^= De;
      BCa = ROL(Abe, 1);
      Agi ^= Di;
      BCe = ROL(Agi, 6);
      Ako ^= Do;
      BCi = ROL(Ako, 25);
      Amu ^= Du;
      BCo = ROL_mult8(Amu, 8);
      Asa ^= Da;
      BCu = ROL(Asa, 18);
      Eka = BCa ^ ((~BCe) & BCi);
      Eke = BCe ^ ((~BCi) & BCo);
      Eki = BCi ^ ((~BCo) & BCu);
      Eko = BCo ^ ((~BCu) & BCa);
      Eku = BCu ^ ((~BCa) & BCe);

      Abu ^= Du;
      BCa = ROL(Abu, 27);
      Aga ^= Da;
      BCe = ROL(Aga, 36);
      Ake ^= De;
      BCi = ROL(Ake, 10);
      Ami ^= Di;
      BCo = ROL(Ami, 15);
      Aso ^= Do;
      BCu = ROL_mult8(Aso, 56);
      Ema = BCa ^ ((~BCe) & BCi);
      Eme = BCe ^ ((~BCi) & BCo);
      Emi = BCi ^ ((~BCo) & BCu);
      Emo = BCo ^ ((~BCu) & BCa);
      Emu = BCu ^ ((~BCa) & BCe);

      Abi ^= Di;
      BCa = ROL(Abi, 62);
      Ago ^= Do;
      BCe = ROL(Ago, 55);
      Aku ^= Du;
      BCi = ROL(Aku, 39);
      Ama ^= Da;
      BCo = ROL(Ama, 41);
      Ase ^= De;
      BCu = ROL(Ase, 2);
      Esa = BCa ^ ((~BCe) & BCi);
      Ese = BCe ^ ((~BCi) & BCo);
      Esi = BCi ^ ((~BCo) & BCu);
      Eso = BCo ^ ((~BCu) & BCa);
      Esu = BCu ^ ((~BCa) & BCe);

      //    prepareTheta
      BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa;
      BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese;
      BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi;
      BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso;
      BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu;

      // thetaRhoPiChiIotaPrepareTheta(round+1, E, A)
      Da = BCu ^ ROL(BCe, 1);
      De = BCa ^ ROL(BCi, 1);
      Di = BCe ^ ROL(BCo, 1);
      Do = BCi ^ ROL(BCu, 1);
      Du = BCo ^ ROL(BCa, 1);

      Eba ^= Da;
      BCa = Eba;
      Ege ^= De;
      BCe = ROL(Ege, 44);
      Eki ^= Di;
      BCi = ROL(Eki, 43);
      Emo ^= Do;
      BCo = ROL(Emo, 21);
      Esu ^= Du;
      BCu = ROL(Esu, 14);
      Aba = BCa ^ ((~BCe) & BCi);
      Aba ^= (tKeccakLane)KeccakF_RoundConstants[round + 1];
      Abe = BCe ^ ((~BCi) & BCo);
      Abi = BCi ^ ((~BCo) & BCu);
      Abo = BCo ^ ((~BCu) & BCa);
      Abu = BCu ^ ((~BCa) & BCe);

      Ebo ^= Do;
      BCa = ROL(Ebo, 28);
      Egu ^= Du;
      BCe = ROL(Egu, 20);
      Eka ^= Da;
      BCi = ROL(Eka, 3);
      Eme ^= De;
      BCo = ROL(Eme, 45);
      Esi ^= Di;
      BCu = ROL(Esi, 61);
      Aga = BCa ^ ((~BCe) & BCi);
      Age = BCe ^ ((~BCi) & BCo);
      Agi = BCi ^ ((~BCo) & BCu);
      Ago = BCo ^ ((~BCu) & BCa);
      Agu = BCu ^ ((~BCa) & BCe);

      Ebe ^= De;
      BCa = ROL(Ebe, 1);
      Egi ^= Di;
      BCe = ROL(Egi, 6);
      Eko ^= Do;
      BCi = ROL(Eko, 25);
      Emu ^= Du;
      BCo = ROL_mult8(Emu, 8);
      Esa ^= Da;
      BCu = ROL(Esa, 18);
      Aka = BCa ^ ((~BCe) & BCi);
      Ake = BCe ^ ((~BCi) & BCo);
      Aki = BCi ^ ((~BCo) & BCu);
      Ako = BCo ^ ((~BCu) & BCa);
      Aku = BCu ^ ((~BCa) & BCe);

      Ebu ^= Du;
      BCa = ROL(Ebu, 27);
      Ega ^= Da;
      BCe = ROL(Ega, 36);
      Eke ^= De;
      BCi = ROL(Eke, 10);
      Emi ^= Di;
      BCo = ROL(Emi, 15);
      Eso ^= Do;
      BCu = ROL_mult8(Eso, 56);
      Ama = BCa ^ ((~BCe) & BCi);
      Ame = BCe ^ ((~BCi) & BCo);
      Ami = BCi ^ ((~BCo) & BCu);
      Amo = BCo ^ ((~BCu) & BCa);
      Amu = BCu ^ ((~BCa) & BCe);

      Ebi ^= Di;
      BCa = ROL(Ebi, 62);
      Ego ^= Do;
      BCe = ROL(Ego, 55);
      Eku ^= Du;
      BCi = ROL(Eku, 39);
      Ema ^= Da;
      BCo = ROL(Ema, 41);
      Ese ^= De;
      BCu = ROL(Ese, 2);
      Asa = BCa ^ ((~BCe) & BCi);
      Ase = BCe ^ ((~BCi) & BCo);
      Asi = BCi ^ ((~BCo) & BCu);
      Aso = BCo ^ ((~BCu) & BCa);
      Asu = BCu ^ ((~BCa) & BCe);
    }

    // copyToState(state, A)
    state[0] = Aba;
    state[1] = Abe;
    state[2] = Abi;
    state[3] = Abo;
    state[4] = Abu;
    state[5] = Aga;
    state[6] = Age;
    state[7] = Agi;
    state[8] = Ago;
    state[9] = Agu;
    state[10] = Aka;
    state[11] = Ake;
    state[12] = Aki;
    state[13] = Ako;
    state[14] = Aku;
    state[15] = Ama;
    state[16] = Ame;
    state[17] = Ami;
    state[18] = Amo;
    state[19] = Amu;
    state[20] = Asa;
    state[21] = Ase;
    state[22] = Asi;
    state[23] = Aso;
    state[24] = Asu;

#undef round
  }
}
